

;底层汇编文件
;原子访问汇编代码
;移植时需要处理, 根据平台修改
;原子访问用到了平台特定的方法, 如果平台没有这些方法, 可以使用关闭中断的实现
;适用于CortexM3 M4F M7
;下面主要使用LDREX与STREX来实现这些功能


  AREA |.text|,CODE,READONLY
  THUMB
  PRESERVE8


  ;对外提供的接口
  GLOBAL H_TS_LL_SetValue
  GLOBAL H_TS_LL_ListPush
  GLOBAL H_TS_LL_ListPop
  GLOBAL H_TS_LL_ValueIncrement
  GLOBAL H_TS_LL_ValueDecrement
  GLOBAL H_TS_LL_WriteIndexIncrement


;变量设置
;R0 变量地址
;R1 设置值
;返回 R0 设置前的值
H_TS_LL_SetValue FUNCTION
  MOV       R2,R0
0
  LDREX     R0,[R2]
  STREX     R3,R1,[R2]
  CMP       R3,#0
  IT        NE
  BNE       %B0
  BX        LR
  ENDFUNC

;链表节点添加操作 不指定添加到头部或尾部 该方法的LL/SC可用CAS替代
;R0 容纳链表头部指针的变量所在地址
;R1 待添加节点的指针
;R2 一个偏移 节点指向下一个节点的相对位置
;typedef struct sItem{struct sList* Next;}Item;
;void push(Item** list,Item* item){
;  H_TS_LL_ListPush(list,item,(size_t)&((Item*)0)->Next);
;}
H_TS_LL_ListPush FUNCTION
  LDREX     R3,[R0]                                               ;List* p=*list;
  STR       R3,[R1,R2]                                            ;item->Next=p;
  STREX     R3,R1,[R2]                                            ;*list=item;//随后p的值代表是否为成功的修改
  CMP       R3,#0                                                 ;if(p!=NULL)
  IT        NE
  BNE       H_TS_LL_ListPush                                      ;{goto H_TS_LL_ListPush;}
  BX        LR                                                    ;return;
  ENDFUNC


;移除一个链表节点 不指定移除哪个部位的节点 H_TS会保证调用该函数过程中节点不会被释放(节点不链表中后才释放) 该方法的LL/SC可用CAS替代
;R0 容纳链表头部指针的变量所在地址
;R1 一个偏移 节点指向下一个节点的相对位置
;返回 R0 被移除的节点地址
;Item* pop(Item** list){
;  return H_TS_LL_ListPop(list,(size_t)&((List*)0)->Next);
;}
H_TS_LL_ListPop FUNCTION
  PUSH      {R4}
  MOV       R2,R0                                                 ;List** list_temp=list;

0
  LDREX     R0,[R2]                                               ;List* p=*list_temp;
  CMP       R0,#0                                                 ;if(p==NULL){
  ITT       EQ
  STREXEQ   R3,R0,[R2]                                            ;//清除LDREX产生的标记
  BEQ       %F99                                                  ;return p;}
  LDR       R3,[R0,R1]                                            ;List* p_next=p->Next;
  STREX     R4,R3,[R2]                                            ;*list=p_next;//随后p_next的值代表是否为成功的修改
  CMP       R4,#0                                                 ;if(p_next!=NULL)
  IT        NE
  BNE       %B0                                                   ;{goto 0;}

99
  POP       {R4}
  BX        LR                                                    ;return p;
  ENDFUNC


;值增加 到达一定值时不增加 该方法的LL/SC可用CAS替代
;R0 待增加变量的地址
;R1 变量最大值(可达到的最大值)
;返回 R0 自增之前的值
;Husize valueIncrement(volatile Husize* ValuePointer,Husize MaxValue){
;   return H_TS_LL_ValueIncrement(ValuePointer);
;}
H_TS_LL_ValueIncrement FUNCTION
  MOV       R2,R0
  PUSH      {R4}

0
  LDREX     R0,[R2]                                               ;Husize Value=*ValuePointer;
                                                                  ;Husize NewValue;
  CMP       R1,R0                                                 ;if(MaxValue>Value){
  ITE       HI                                                    ;  NewValue=Value+1;
  ADDHI     R3,R0,#1                                              ;}else
  MOVLS     R3,R0                                                 ;{NewValue=Value;}

  STREX     R4,R3,[R2]                                            ;*ValuePointer=NewValue;//STREX
  CMP       R4,#0                                                 ;//判断是否保存成功
  IT        NE
  BNE       %B0                                                   ;//保存失败 重试
  
  POP       {R4}
  BX        LR
  ENDFUNC


;值自减 到达0时不减少 该方法的LL/SC可用CAS替代
;R0 待自减变量的地址
;返回 R0 自减之前的值
;Husize valueDecrement(volatile Husize* ValuePointer){
;  return H_TS_LL_ValueDecrement(ValuePointer);
;}
H_TS_LL_ValueDecrement FUNCTION
  MOV       R2,R0

0
  LDREX     R0,[R2]                                               ;Husize Value=*ValuePointer;
                                                                  ;Husize NewValue;
  CMP       R0,#0                                                 ;if(Value>0)
  ITE       HI                                                    ;  NewValue=Value-1;
  SUBHI     R3,R0,#1                                              ;}else
  MOVLS     R3,R0                                                 ;{NewValue=Value;}

  STREX     R1,R3,[R2]                                            ;*ValuePointer=NewValue;//STREX
  CMP       R1,#0                                                 ;//判断是否保存成功
  IT        NE
  BNE       %B0                                                   ;//保存失败 重试
  
  BX        LR
  ENDFUNC


;FIFO索引增加 该方法的LL/SC不可用CAS替代
;R0: 指向写索引的指针
;R1: 指向读索引的指针
;R2: FIFO大小 索引范围为[0,R2)
;返回 R0 大于等于0: 自增成功, 为待写入的起始索引 -1:无自增空间
;Hsize writeIndexIncrement(volatile Hsize* WriteIndexPointer,volatile Hsize* ReadIndexPointer,Hsize Size){
;  return H_TS_LL_WriteIndexIncrement(WriteIndexPointer,ReadIndex,Size);
;}
H_TS_LL_WriteIndexIncrement FUNCTION
  PUSH      {R4,R5}

0
  LDREX     R3,[R0]                                               ;Hsize WriteIndex=*WriteIndexPointer;
  LDR       R4,[R1]                                               ;Hsize ReadIndex=*ReadIndexPointer;
  SUBS      R4,R3                                                 ;Hsize size=(ReadIndex-=WriteIndex);
  IT        GE
  BGE       %F1                                                   ;if(size>=0){goto 1;}

  SUB       R4,R2,R4                                              ;size=Size-size;
1
  CMP       R4,#1                                                 ;if(size<=1){
  ITTT      LE                                                    ;  
  STREXLE   R4,R3,[R0]                                            ;  清除LDREX产生的标记
  MVNLE     R0,#0                                                 ;  retval=-1;
  BLE       %F99                                                  ;  goto 99;//return retval;
                                                                  ;}

  ADD       R4,R3,#1                                              ;Hsize writeIndex=WriteIndex+1;
  CMP       R4,R2                                                 ;if(writeIndex>=Size){
  IT        GE                                                    ;
  MOVGE     R4,#0                                                 ;  writeIndex=0;
                                                                  ;}

  STREX     R5,R4,[R0]                                            ;*WriteIndexPointer=writeIndex;
  CMP       R5,#0
  IT        NE
  BNE       %B0

  MOV       R0,R3                                                 ;return WriteIndex;

99
  POP       {R4,R5}
  BX        LR
  ENDFUNC


  ALIGN     4

  END

